/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     
 * Description  :     
 * Change log   :     
 * To Do        :
 ************************************************************************/
using System;
using SMLib.Automata.Base;
using System.Collections.Generic;
using SMLib.Automata.IA;
using System.Threading;

namespace SMLib
{
	/// <summary>
	/// Wrapper proxy for a remote IA
	/// </summary>
	public class SUL_IA_WrapperProxy: SMLib.Automata.IA.IBlackBox_IA
	{
		//TODO make this as a parameter of configuration
		const int MAX_WAIT_FOR_EVENT = 500;
		//500 millisecond
		TcpHelper helper;
		Alphabet alphabet;
		List<TcpHelper.PACKET> recievedPack;
		Mutex mx;
		bool debugMode = true;

		/// <summary>
		/// Initializes a new instance of the <see cref="SMLib.SUL_IA_WrapperProxy"/> class.
		/// </summary>
		/// <param name="port">remote tcp port</param>
		public SUL_IA_WrapperProxy (int port, string server="localhost")
		{
			Console.WriteLine ("Listening into port " + port);
			helper = new TcpHelper (server, port, true, Rec);
			alphabet = null;
			helper.Init ();
			recievedPack = new List<TcpHelper.PACKET> ();
			mx = new Mutex ();
		}

		/// <summary>
		/// Close the TCP connection
		/// </summary>
		public void Close ()
		{
			helper.Close ();
		}

		/// <summary>
		/// handler for recieving a packet
		/// </summary>
		/// <param name="p">the recieved packet</param>
		void Rec (TcpHelper.PACKET p)
		{
			//Console.Write ("***Rec***");
			mx.WaitOne ();
			recievedPack.Add (p);
			mx.ReleaseMutex ();
		}
		#region IBlackBox_IA implementation
		public void Reset ()
		{
			TcpHelper.PACKET p = new TcpHelper.PACKET ();
			p.TYPE = 1;
			//TcpHelper.PACKET q = 
			helper.SendPacket (p);
			//Console.WriteLine ("Answer is: " + q.ToString ());
			//* we wait for any observable event from last session!
			Thread.Sleep (MAX_WAIT_FOR_EVENT * 2);
			this.recievedPack.Clear ();
			if (debugMode)
				Console.Write ("\n/Reset/");
		}

		public void Perform (ISymbol input)
		{
			if (EventAvailable ()) {
				//Console.Write ("!" + input);
				throw new NotEnabledActionException ("");
			}

			if (debugMode)
				Console.Write ("/" + input);
			TcpHelper.PACKET p = new TcpHelper.PACKET ();
			p.CONTENT = input.ToString ();
			p.TYPE = 102;
			//Console.WriteLine ("Send input. Packet is: " + p.ToString ());
			TcpHelper.PACKET q = helper.SendPacket (p);
			if (q.TYPE == TcpHelper.PACKET.TYPE_NOK) {
				if (debugMode)
					Console.Write ("!!");
				throw new NotEnabledActionException ("");
			}
		}

		public bool EventAvailable ()
		{
			//* optimization! if we have already some event, don't wait!
			if (recievedPack.Count > 0) {
				return true;
			}

			//* wait for an output [SUL initiated response]
			//TODO this is the maximum time, maybe we can divide it and wait less time [reapetedly]
			Thread.Sleep (MAX_WAIT_FOR_EVENT);
			bool p;
			mx.WaitOne ();
			if (recievedPack.Count == 0)
				p = false;
			else {
				p = true;
			}
			mx.ReleaseMutex ();
			return p;
		}

		public ISymbol Event ()
		{
			TcpHelper.PACKET p;
			mx.WaitOne ();
			if (recievedPack.Count == 0)
				p = null;
			else {
				p = recievedPack [0];
				recievedPack.RemoveAt (0);
			}
			mx.ReleaseMutex ();
			if (p == null)
				return null;
			if (debugMode)
				Console.Write ("/O_" + p.CONTENT);
			return new SMLib.Automata.Base.StringSymbol (p.CONTENT);
		}

		public Alphabet InputAlphabet {
			get {
				if (alphabet == null) {
					TcpHelper.PACKET p = new TcpHelper.PACKET ();
					p.TYPE = 8;
					TcpHelper.PACKET q = helper.SendPacket (p);
					String[] symbs = q.CONTENT.Split (';');
					alphabet = new Alphabet ();
					foreach (string sym in symbs) {
						if (sym == "")
							continue;					
						alphabet.AddSymbol (new StringSymbol (sym));
					}

					Console.WriteLine ("Alphabet is " + alphabet);
					return alphabet;	
				} else
					return alphabet;
			}
		}
		#endregion
	}
}

